package com.definity.toolkit.ui.widget;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;

import com.definity.toolkit.ui.Body;
import com.definity.toolkit.ui.style.Style;
import com.definity.toolkit.ui.widget.support.CalendarSupport;

public class CalendarPanel extends JPanel implements Widget<CalendarPanel> {

	private static final long serialVersionUID = -2344040255623011380L;

	private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("dd");
	private static final SimpleDateFormat fullDateFormatter = new SimpleDateFormat("dd/MM/yyyy");
	
	private final DateLabel[][] days = new DateLabel[6][7];
	private final CalendarSupport calendarSupport;
	
	public CalendarPanel() {
		this(new CalendarSupport());
	}
	
	public CalendarPanel(CalendarSupport calendarSupport) {
		this.calendarSupport = calendarSupport;
		
		setLayout(new GridLayout(7, 7, 0, 0));
		setBorder(BorderFactory.createLineBorder(Color.BLACK));
		setOpaque(false);
		
		calendarSupport.setNormalStyle(getNormalDayStyle());
		calendarSupport.setAnotherMonthStyle(getAnotherMonthDayStyle());
		calendarSupport.setWeekendStyle(getWeekendDayStyle());
		calendarSupport.setSelectedStyle(getSelectedDayStyle());
		
		Style headerStyle = getHeaderStyle();

		String[] weekDays = { "D", "S", "T", "Q", "Q", "S", "S" };
		
		for (int i = 0; i < weekDays.length; i++) {
			JLabel headerDay = new JLabel(weekDays[i]);
			headerDay.setOpaque(true);
			headerStyle.apply(headerDay);
			add(headerDay);
		}
		
		initDays();
	}
	
	public CalendarSupport getCalendarSupport() {
		return calendarSupport;
	}

	private void initDays() {
		Calendar firstCalendar = calendarSupport.getBaseDate();
		firstCalendar.set(Calendar.DATE, 1);
		
		int dayOfWeek = firstCalendar.get(Calendar.DAY_OF_WEEK);
		
		firstCalendar.add(Calendar.DATE, -dayOfWeek + 1);
		
		for (int i = 0; i < days.length; i++) {
			DateLabel[] week = days[i];
			for (int j = 0; j < week.length; j++) {
				DateLabel day = new DateLabel(firstCalendar.getTime());
				add(day);

				firstCalendar.add(Calendar.DATE, 1);

				days[i][j] = day;
			}
		}
	}

	private Style getHeaderStyle() {
		Style style = new Style("calendar.header");
		style.alignment(Style.CENTER);
		style.background(Color.DARK_GRAY);
		style.foreground(Color.WHITE);
		return style;
	}

	private Style getNormalDayStyle() {
		Style style = new Style("calendar.normal");
		style.border(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
		style.alignment(Style.CENTER);
		style.foreground(Color.BLACK);
		style.opaque(false);
		return style;
	}

	private Style getAnotherMonthDayStyle() {
		Style style = new Style("calendar.anotherMonth");
		style.border(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
		style.alignment(Style.CENTER);
		style.opaque(false);
		style.foreground(Color.LIGHT_GRAY);
		return style;
	}

	private Style getWeekendDayStyle() {
		Style style = new Style("calendar.weekend");
		style.alignment(Style.CENTER);
		style.border(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
		style.foreground(Color.RED);
		style.opaque(false);
		return style;
	}

	private Style getSelectedDayStyle() {
		Style style = new Style("calendar.selected");
		style.border(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
		style.alignment(Style.CENTER);
		style.background(Color.BLUE);
		style.foreground(Color.WHITE);
		style.opaque(true);
		return style;
	}

	class DateLabel extends JLabel implements PropertyChangeListener {
		
		private static final long serialVersionUID = -6169231044794039205L;
		
		private final Date date;

		public DateLabel(Date date) {
			super(dateFormatter.format(date.getTime()));
			this.date = date;
			
			setToolTipText(fullDateFormatter.format(date.getTime()));
			
			calendarSupport.addPropertyChangeListener(this);
			
			Style style = calendarSupport.getStyle(date);
			style.apply(this);
			
			addMouseListener(new DateSelectionMouseListener(calendarSupport));
		}
		
		public Date getDate() {
			return date;
		}

		@Override
		public void propertyChange(PropertyChangeEvent evt) {
			Style style = calendarSupport.getStyle(date);
			style.apply(this);
		}
		
	}
	
	class DateSelectionMouseListener extends MouseAdapter {
		
		private final CalendarSupport dCalendar;

		public DateSelectionMouseListener(CalendarSupport dCalendar) {
			this.dCalendar = dCalendar;
		}
		
		@Override
		public void mouseClicked(MouseEvent e) {
			DateLabel dateLabel = (DateLabel) e.getSource();
			if (!dateLabel.isEnabled())
				return;
			
			Date date = dateLabel.getDate();
			
			dCalendar.setDateSelected(date);
		}
		
	}

	@Override
	public String id() {
		return getName();
	}

	@Override
	public CalendarPanel id(String id) {
		setName(id);
		return this;
	}

	@Override
	public CalendarPanel properties(Body<CalendarPanel> body) {
		body.content(this);
		return this;
	}

	@Override
	public CalendarPanel size(int width, int height) {
		setPreferredSize(new Dimension(width, height));
		return this;
	}

	@Override
	public Color background() {
		return getBackground();
	}

	@Override
	public CalendarPanel background(Color background) {
		setBackground(background);
		return this;
	}

	@Override
	public Color foreground() {
		return getForeground();
	}

	@Override
	public CalendarPanel foreground(Color foreground) {
		setForeground(foreground);
		return this;
	}

	@Override
	public Font font() {
		return getFont();
	}

	@Override
	public CalendarPanel font(Font font) {
		setFont(font);
		return this;
	}
	
}
